﻿using System;
using System.IO;
using System.Text;

namespace SecurityModule
{
    class RequestFilter : Stream
    {
        //Temporary buffer to accumulate page writes
        private MemoryStream _ms;

        //Handle to original input stream pipeline
        private readonly Stream _inputStream;

        //Encoding of the response
        private readonly Encoding _encoding;

        //The callback function
        private readonly Func<string, string> _callback;
        private readonly Func<Stream, Stream, bool> _streamingCallback;

        /// <summary>
        /// String based callback filter
        /// </summary>
        /// <param name="stream"></param>
        /// <param name="encoding"></param>
        /// <param name="callback"></param>
        public RequestFilter(Stream stream, Encoding encoding, Func<string, string> callback)
        {
            _inputStream = stream;
            _encoding = encoding;
            _callback = callback;
        }

        /// <summary>
        /// Streaming callback filter
        /// </summary>
        /// <param name="stream"></param>
        /// <param name="callback"></param>
        public RequestFilter(Stream stream, Func<Stream, Stream, bool> callback)
        {
            _inputStream = stream;
            _streamingCallback = callback;
        }

        public override void Flush()
        {
            //Decide if streaming or string based filtering
            _ms.Flush();

            //if (_streamingCallback != null)
            //{
            //    ms.Seek(0, SeekOrigin.Begin);
            //    var stream = _streamingCallback(ms);
            //    stream.Seek(0, SeekOrigin.Begin);
            //    stream.CopyTo(_outputStream);
            //    _outputStream.Flush();
            //} else if (_callback != null)
            //{
            //    //perform replacement
            //    string content = _encoding.GetString(ms.GetBuffer());

            //    //Perform the content replacement routine
            //    content = _callback(content);

            //    Byte[] bytes = _encoding.GetBytes(content);
            //    _outputStream.Write(bytes, 0, bytes.Length);
            //    _outputStream.Flush();
            //}



            //This is a passthrough filter
            //ms.Seek(0, SeekOrigin.Begin);
            //ms.CopyTo(_outputStream);
            //_outputStream.Flush();
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            return _ms.Seek(offset, origin);
        }

        public override void SetLength(long value)
        {
            _ms.SetLength(value);
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            if (_ms == null)
            {
                //Fill ms from callback function
                if (_callback != null)
                {
                    //I should read stream, execute callback, fill ms.
                    var sr = new StreamReader(_inputStream, _encoding);
                    var content = sr.ReadToEnd();

                    //Perform the content replacement routine
                    content = _callback(content);

                    var bytes = _encoding.GetBytes(content);
                    _ms = new MemoryStream();
                    _ms.Write(bytes, 0, bytes.Length);
                    _ms.Seek(0, SeekOrigin.Begin);
                }
                else if (_streamingCallback != null)
                {
                    _ms = new MemoryStream();
                    _streamingCallback(_inputStream, _ms);
                    _ms.Seek(0, SeekOrigin.Begin);
                }

            }
            //else
            //{
            //    //just curious
            //    var sr = new StreamReader(_outputStream, _encoding);
            //    string content = sr.ReadToEnd();
            //}


            if (_ms != null) 
                return _ms.Read(buffer, offset, count);
            throw new ObjectDisposedException("_ms");
        }

        public override void Write(byte[] buffer, int offset, int count)
        {

            throw new NotSupportedException();
            //ms.Write(buffer, offset, count); 
            //_outputStream.Write(buffer, offset, count);

        }

        public override bool CanRead
        {
            get { return true; }
        }

        public override bool CanSeek
        {
            get { return false; }
        }

        public override bool CanWrite
        {
            get { return false; }
        }

        public override long Length
        {
            get { return _ms.Length; }
        }

        public override long Position
        {
            get { return _ms.Position; }
            set { throw new NotSupportedException(); }
        }
    }
}
